పైథాన్ యొక్క అబ్స్ట్రాక్ట్ బేస్ క్లాసెస్ (ABCs) శక్తిని అన్లాక్ చేయండి. ప్రోటోకాల్-ఆధారిత స్ట్రక్చరల్ టైపింగ్ మరియు ఫార్మల్ ఇంటర్ఫేస్ డిజైన్ మధ్య కీలకమైన వ్యత్యాసాన్ని తెలుసుకోండి.
పైథాన్ అబ్స్ట్రాక్ట్ బేస్ క్లాసెస్: ప్రోటోకాల్ ఇంప్లిమెంటేషన్ వర్సెస్ ఇంటర్ఫేస్ డిజైన్లో నైపుణ్యం
సాఫ్ట్వేర్ డెవలప్మెంట్ ప్రపంచంలో, దృఢమైన, నిర్వహించదగిన మరియు విస్తరించగల అప్లికేషన్లను నిర్మించడం అంతిమ లక్ష్యం. ప్రాజెక్టులు కొన్ని స్క్రిప్ట్ల నుండి అంతర్జాతీయ బృందాలచే నిర్వహించబడే సంక్లిష్ట వ్యవస్థలుగా పెరిగేకొద్దీ, స్పష్టమైన నిర్మాణం మరియు ఊహించదగిన ఒప్పందాల అవసరం చాలా ముఖ్యమైనదిగా మారుతుంది. వేర్వేరు డెవలపర్లచే, వేర్వేరు టైమ్ జోన్లలో వ్రాయబడిన వేర్వేరు భాగాలు, సజావుగా మరియు విశ్వసనీయంగా ఎలా సంకర్షణ చెందుతాయని మనం ఎలా నిర్ధారించుకోవాలి? దీనికి సమాధానం అబ్స్ట్రాక్షన్ (వియుక్తత) సూత్రంలో ఉంది.
పైథాన్, దాని డైనమిక్ స్వభావంతో, అబ్స్ట్రాక్షన్ కోసం ఒక ప్రసిద్ధ తత్వాన్ని కలిగి ఉంది: "డక్ టైపింగ్". ఒక వస్తువు బాతులా నడిచి, బాతులా కూస్తే, దానిని మనం బాతుగా పరిగణిస్తాము. ఈ సౌలభ్యం పైథాన్ యొక్క గొప్ప బలాలలో ఒకటి, ఇది వేగవంతమైన అభివృద్ధిని మరియు శుభ్రమైన, చదవగలిగే కోడ్ను ప్రోత్సహిస్తుంది. అయితే, పెద్ద-స్థాయి అప్లికేషన్లలో, కేవలం పరోక్ష ఒప్పందాలపై ఆధారపడటం సూక్ష్మమైన బగ్లకు మరియు నిర్వహణ తలనొప్పులకు దారితీస్తుంది. ఒక 'బాతు' అనుకోకుండా ఎగరలేకపోతే ఏమి జరుగుతుంది? ఇక్కడే పైథాన్ యొక్క అబ్స్ట్రాక్ట్ బేస్ క్లాసెస్ (ABCs) రంగప్రవేశం చేస్తాయి, పైథాన్ యొక్క డైనమిక్ స్ఫూర్తిని త్యాగం చేయకుండా అధికారిక ఒప్పందాలను సృష్టించడానికి ఒక శక్తివంతమైన యంత్రాంగాన్ని అందిస్తాయి.
కానీ ఇక్కడ ఒక కీలకమైన మరియు తరచుగా తప్పుగా అర్థం చేసుకోబడిన వ్యత్యాసం ఉంది. పైథాన్లో ABCలు అన్నింటికీ సరిపోయే ఒకే సాధనం కాదు. అవి సాఫ్ట్వేర్ డిజైన్ యొక్క రెండు విభిన్నమైన, శక్తివంతమైన తత్వాలకు సేవ చేస్తాయి: వారసత్వాన్ని డిమాండ్ చేసే స్పష్టమైన, అధికారిక ఇంటర్ఫేస్లను సృష్టించడం, మరియు సామర్థ్యాలను తనిఖీ చేసే సౌకర్యవంతమైన ప్రోటోకాల్లను నిర్వచించడం. ఈ రెండు విధానాల మధ్య వ్యత్యాసాన్ని అర్థం చేసుకోవడం—ఇంటర్ఫేస్ డిజైన్ వర్సెస్ ప్రోటోకాల్ ఇంప్లిమెంటేషన్—పైథాన్లో ఆబ్జెక్ట్-ఓరియంటెడ్ డిజైన్ యొక్క పూర్తి సామర్థ్యాన్ని అన్లాక్ చేయడానికి మరియు సౌకర్యవంతంగా మరియు సురక్షితంగా ఉండే కోడ్ను వ్రాయడానికి కీలకం. ఈ గైడ్ రెండు తత్వాలను అన్వేషిస్తుంది, మీ గ్లోబల్ సాఫ్ట్వేర్ ప్రాజెక్ట్లలో ప్రతి విధానాన్ని ఎప్పుడు ఉపయోగించాలో ఆచరణాత్మక ఉదాహరణలు మరియు స్పష్టమైన మార్గదర్శకత్వం అందిస్తుంది.
ఫార్మాటింగ్ పై ఒక గమనిక: నిర్దిష్ట ఫార్మాటింగ్ పరిమితులకు కట్టుబడి ఉండటానికి, ఈ ఆర్టికల్లోని కోడ్ ఉదాహరణలు బోల్డ్ మరియు ఇటాలిక్ స్టైల్స్ ఉపయోగించి ప్రామాణిక టెక్స్ట్ ట్యాగ్లలో ప్రదర్శించబడ్డాయి. ఉత్తమ పఠనీయత కోసం వాటిని మీ ఎడిటర్లోకి కాపీ చేసుకోవాలని మేము సిఫార్సు చేస్తున్నాము.
పునాది: అసలు అబ్స్ట్రాక్ట్ బేస్ క్లాసెస్ అంటే ఏమిటి?
రెండు డిజైన్ తత్వాలలోకి ప్రవేశించే ముందు, ఒక పటిష్టమైన పునాదిని ఏర్పాటు చేసుకుందాం. అబ్స్ట్రాక్ట్ బేస్ క్లాస్ అంటే ఏమిటి? దాని మూలంలో, ఒక ABC ఇతర క్లాస్ల కోసం ఒక బ్లూప్రింట్. ఇది ఏ అనుగుణమైన సబ్క్లాస్ అయినా తప్పనిసరిగా అమలు చేయవలసిన మెథడ్స్ మరియు ప్రాపర్టీల సమితిని నిర్వచిస్తుంది. ఇది "ఈ కుటుంబంలో భాగంగా చెప్పుకునే ఏ క్లాస్ అయినా ఈ నిర్దిష్ట సామర్థ్యాలను కలిగి ఉండాలి" అని చెప్పే ఒక మార్గం.
పైథాన్ యొక్క అంతర్నిర్మిత `abc` మాడ్యూల్ ABCలను సృష్టించడానికి సాధనాలను అందిస్తుంది. రెండు ప్రాథమిక భాగాలు:
- `ABC`: ABCని సృష్టించడానికి మెటాక్లాస్గా ఉపయోగించే ఒక సహాయక క్లాస్. ఆధునిక పైథాన్లో (3.4+), మీరు కేవలం `abc.ABC` నుండి వారసత్వంగా పొందవచ్చు.
- `@abstractmethod`: మెథడ్స్ను అబ్స్ట్రాక్ట్గా గుర్తించడానికి ఉపయోగించే ఒక డెకరేటర్. ABC యొక్క ఏ సబ్క్లాస్ అయినా ఈ మెథడ్స్ను తప్పనిసరిగా అమలు చేయాలి.
ABCలను నియంత్రించే రెండు ప్రాథమిక నియమాలు ఉన్నాయి:
- అమలు చేయని అబ్స్ట్రాక్ట్ మెథడ్స్ ఉన్న ABC యొక్క ఇన్స్టాన్స్ ను మీరు సృష్టించలేరు. ఇది ఒక టెంప్లేట్, పూర్తి చేసిన ఉత్పత్తి కాదు.
- ఏ కాంక్రీట్ సబ్క్లాస్ అయినా వారసత్వంగా పొందిన అన్ని అబ్స్ట్రాక్ట్ మెథడ్స్ను తప్పనిసరిగా అమలు చేయాలి. అలా చేయడంలో విఫలమైతే, అది కూడా ఒక అబ్స్ట్రాక్ట్ క్లాస్ అవుతుంది, మరియు మీరు దాని ఇన్స్టాన్స్ ను సృష్టించలేరు.
మీడియా ఫైళ్లను నిర్వహించడానికి ఒక సిస్టమ్తో ఒక క్లాసిక్ ఉదాహరణతో దీనిని ఆచరణలో చూద్దాం.
ఉదాహరణ: ఒక సాధారణ MediaFile ABC
వివిధ రకాల మీడియాను నిర్వహించాల్సిన అప్లికేషన్ను మనం నిర్మిస్తున్నామని ఊహించుకోండి. ఫార్మాట్తో సంబంధం లేకుండా ప్రతి మీడియా ఫైల్ ప్లే చేయగలగాలి మరియు కొంత మెటాడేటాను కలిగి ఉండాలని మనకు తెలుసు. మనం ఈ ఒప్పందాన్ని ఒక ABCతో నిర్వచించవచ్చు.
import abc
class MediaFile(abc.ABC):
def __init__(self, filepath: str):
self.filepath = filepath
print(f"Base init for {self.filepath}")
@abc.abstractmethod
def play(self) -> None:
"""Play the media file."""
raise NotImplementedError
@abc.abstractmethod
def get_metadata(self) -> dict:
"""Return a dictionary of media metadata."""
raise NotImplementedError
మనం నేరుగా `MediaFile` యొక్క ఇన్స్టాన్స్ ను సృష్టించడానికి ప్రయత్నిస్తే, పైథాన్ మనల్ని ఆపుతుంది:
# ఇది TypeError ను రైజ్ చేస్తుంది
# media = MediaFile("path/to/somefile.txt")
# TypeError: Can't instantiate abstract class MediaFile with abstract methods get_metadata, play
ఈ బ్లూప్రింట్ను ఉపయోగించడానికి, మనం `play()` మరియు `get_metadata()` కోసం ఇంప్లిమెంటేషన్లను అందించే కాంక్రీట్ సబ్క్లాస్లను సృష్టించాలి.
class AudioFile(MediaFile):
def play(self) -> None:
print(f"Playing audio from {self.filepath}...")
def get_metadata(self) -> dict:
return {"codec": "mp3", "duration_seconds": 180}
class VideoFile(MediaFile):
def play(self) -> None:
print(f"Playing video from {self.filepath}...")
def get_metadata(self) -> dict:
return {"codec": "h264", "resolution": "1920x1080"}
ఇప్పుడు, మనం `AudioFile` మరియు `VideoFile` యొక్క ఇన్స్టాన్స్ లను సృష్టించవచ్చు ఎందుకంటే అవి `MediaFile` ద్వారా నిర్వచించబడిన ఒప్పందాన్ని నెరవేరుస్తాయి. ఇది ABCల యొక్క ప్రాథమిక యంత్రాంగం. కానీ నిజమైన శక్తి మనం ఈ యంత్రాంగాన్ని *ఎలా* ఉపయోగిస్తాము అనే దాని నుండి వస్తుంది.
మొదటి తత్వం: ఫార్మల్ ఇంటర్ఫేస్ డిజైన్గా ABCలు (నామినల్ టైపింగ్)
ABCలను ఉపయోగించడానికి మొదటి మరియు అత్యంత సాంప్రదాయ మార్గం ఫార్మల్ ఇంటర్ఫేస్ డిజైన్. ఈ విధానం నామినల్ టైపింగ్లో పాతుకుపోయింది, ఇది జావా, సి++, లేదా సి# వంటి భాషల నుండి వచ్చే డెవలపర్లకు సుపరిచితమైన భావన. నామినల్ సిస్టమ్లో, ఒక టైప్ యొక్క అనుకూలత దాని పేరు మరియు స్పష్టమైన డిక్లరేషన్ ద్వారా నిర్ణయించబడుతుంది. మన సందర్భంలో, ఒక క్లాస్ `MediaFile` ABC నుండి స్పష్టంగా వారసత్వం పొందినట్లయితే మాత్రమే అది `MediaFile`గా పరిగణించబడుతుంది.
దీనిని ఒక ప్రొఫెషనల్ సర్టిఫికేషన్ లాగా ఆలోచించండి. సర్టిఫైడ్ ప్రాజెక్ట్ మేనేజర్ కావడానికి, మీరు కేవలం అలా ప్రవర్తిస్తే సరిపోదు; మీరు తప్పనిసరిగా అధ్యయనం చేసి, ఒక నిర్దిష్ట పరీక్షలో ఉత్తీర్ణులై, మీ అర్హతను స్పష్టంగా పేర్కొనే అధికారిక సర్టిఫికేట్ను పొందాలి. మీ సర్టిఫికేషన్ పేరు మరియు వంశం ముఖ్యం.
ఈ మోడల్లో, ABC ఒక చర్చకురాని ఒప్పందంగా పనిచేస్తుంది. దాని నుండి వారసత్వం పొందడం ద్వారా, ఒక క్లాస్ సిస్టమ్లోని మిగిలిన భాగాలకు అవసరమైన కార్యాచరణను అందిస్తానని అధికారిక వాగ్దానం చేస్తుంది.
ఉదాహరణ: ఒక డేటా ఎక్స్పోర్టర్ ఫ్రేమ్వర్క్
వినియోగదారులను వివిధ ఫార్మాట్లలోకి డేటాను ఎగుమతి చేయడానికి అనుమతించే ఒక ఫ్రేమ్వర్క్ను మనం నిర్మిస్తున్నామని ఊహించుకోండి. ప్రతి ఎక్స్పోర్టర్ ప్లగిన్ కఠినమైన నిర్మాణానికి కట్టుబడి ఉందని మనం నిర్ధారించుకోవాలనుకుంటున్నాము. మనం ఒక `DataExporter` ఇంటర్ఫేస్ను నిర్వచించవచ్చు.
import abc
from datetime import datetime
class DataExporter(abc.ABC):
"""డేటా ఎగుమతి క్లాస్ల కోసం ఒక ఫార్మల్ ఇంటర్ఫేస్."""
@abc.abstractmethod
def export(self, data: list[dict]) -> str:
"""డేటాను ఎగుమతి చేసి, ఒక స్టేటస్ మెసేజ్ను తిరిగి ఇస్తుంది."""
pass
def get_timestamp(self) -> str:
"""అన్ని సబ్క్లాస్లచే పంచుకోబడిన ఒక కాంక్రీట్ సహాయక మెథడ్."""
return datetime.utcnow().isoformat()
class CSVExporter(DataExporter):
def export(self, data: list[dict]) -> str:
filename = f"export_{self.get_timestamp()}.csv"
print(f"Exporting {len(data)} rows to {filename}")
# ... అసలు CSV వ్రాసే లాజిక్ ...
return f"Successfully exported to {filename}"
class JSONExporter(DataExporter):
def export(self, data: list[dict]) -> str:
filename = f"export_{self.get_timestamp()}.json"
print(f"Exporting {len(data)} records to {filename}")
# ... అసలు JSON వ్రాసే లాజిక్ ...
return f"Successfully exported to {filename}"
ఇక్కడ, `CSVExporter` మరియు `JSONExporter` స్పష్టంగా మరియు ధృవీకరించదగిన `DataExporter`లు. మన అప్లికేషన్ యొక్క కోర్ లాజిక్ ఈ ఒప్పందంపై సురక్షితంగా ఆధారపడవచ్చు:
def run_export_process(exporter: DataExporter, data_to_export: list[dict]):
print("--- Starting export process ---")
if not isinstance(exporter, DataExporter):
raise TypeError("Exporter must be a valid DataExporter implementation.")
status = exporter.export(data_to_export)
print(f"Process finished with status: {status}")
# Usage
data = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
run_export_process(CSVExporter(), data)
run_export_process(JSONExporter(), data)
ABC ఒక కాంక్రీట్ మెథడ్, `get_timestamp()`ను కూడా అందిస్తుందని గమనించండి, ఇది దాని అన్ని పిల్లలకు షేర్డ్ ఫంక్షనాలిటీని అందిస్తుంది. ఇది ఇంటర్ఫేస్-ఆధారిత డిజైన్లో ఒక సాధారణ మరియు శక్తివంతమైన ప్యాటర్న్.
ఫార్మల్ ఇంటర్ఫేస్ విధానం యొక్క లాభాలు మరియు నష్టాలు
లాభాలు:
- సుస్పష్టమైనది మరియు స్పష్టమైనది: ఒప్పందం చాలా స్పష్టంగా ఉంటుంది. ఒక డెవలపర్ `class CSVExporter(DataExporter):` అనే వారసత్వ లైన్ను చూసి, ఆ క్లాస్ యొక్క పాత్ర మరియు సామర్థ్యాలను తక్షణమే అర్థం చేసుకోగలరు.
- టూలింగ్-స్నేహపూర్వకమైనది: IDEలు, లింటర్లు, మరియు స్టాటిక్ ఎనాలిసిస్ టూల్స్ సులభంగా ఒప్పందాన్ని ధృవీకరించగలవు, అద్భుతమైన ఆటోకంప్లీషన్ మరియు ఎర్రర్ చెకింగ్ను అందిస్తాయి.
- షేర్డ్ ఫంక్షనాలిటీ: ABCలు కాంక్రీట్ మెథడ్స్ను అందించగలవు, నిజమైన బేస్ క్లాస్గా పనిచేస్తాయి మరియు కోడ్ డూప్లికేషన్ను తగ్గిస్తాయి.
- పరిచయం: ఈ ప్యాటర్న్ ఇతర ఆబ్జెక్ట్-ఓరియంటెడ్ భాషల యొక్క అధిక భాగం డెవలపర్లకు తక్షణమే గుర్తించదగినది.
నష్టాలు:
- గట్టి కలయిక: కాంక్రీట్ క్లాస్ ఇప్పుడు నేరుగా ABCతో ముడిపడి ఉంటుంది. ABCని తరలించవలసి వస్తే లేదా మార్చవలసి వస్తే, అన్ని సబ్క్లాస్లు ప్రభావితమవుతాయి.
- దృఢత్వం: ఇది ఒక కఠినమైన క్రమానుగత సంబంధాన్ని బలవంతం చేస్తుంది. ఒక క్లాస్ తార్కికంగా ఒక ఎక్స్పోర్టర్గా పనిచేయగలిగితే, కానీ ఇప్పటికే వేరే, అవసరమైన బేస్ క్లాస్ నుండి వారసత్వం పొందితే ఏమిటి? పైథాన్ యొక్క బహుళ వారసత్వం దీనిని పరిష్కరించగలదు, కానీ అది దాని స్వంత సంక్లిష్టతలను కూడా ప్రవేశపెట్టగలదు (డైమండ్ ప్రాబ్లమ్ లాగా).
- చొరబాటు స్వభావం: దీనిని థర్డ్-పార్టీ కోడ్ను స్వీకరించడానికి ఉపయోగించలేరు. మీరు `export()` మెథడ్తో ఒక క్లాస్ను అందించే లైబ్రరీని ఉపయోగిస్తుంటే, దానిని సబ్క్లాస్ చేయకుండా `DataExporter`గా మార్చలేరు (ఇది సాధ్యం కాకపోవచ్చు లేదా కోరదగినది కాకపోవచ్చు).
రెండవ తత్వం: ప్రోటోకాల్ ఇంప్లిమెంటేషన్గా ABCలు (స్ట్రక్చరల్ టైపింగ్)
రెండవ, మరింత "పైథానిక్" తత్వం డక్ టైపింగ్తో సరిపోలుతుంది. ఈ విధానం స్ట్రక్చరల్ టైపింగ్ను ఉపయోగిస్తుంది, ఇక్కడ అనుకూలత పేరు లేదా వంశం ద్వారా కాకుండా, నిర్మాణం మరియు ప్రవర్తన ద్వారా నిర్ణయించబడుతుంది. ఒక వస్తువుకు పని చేయడానికి అవసరమైన మెథడ్స్ మరియు అట్రిబ్యూట్స్ ఉంటే, దాని డిక్లేర్డ్ క్లాస్ హైరార్కీతో సంబంధం లేకుండా, ఆ పనికి సరైన టైప్గా పరిగణించబడుతుంది.
ఈత కొట్టే సామర్థ్యం గురించి ఆలోచించండి. ఈతగాడిగా పరిగణించబడటానికి, మీకు సర్టిఫికేట్ అవసరం లేదు లేదా "ఈతగాడు" కుటుంబ వృక్షంలో భాగం కానవసరం లేదు. మీరు మునిగిపోకుండా నీటిలో మిమ్మల్ని మీరు ముందుకు నడిపించుకోగలిగితే, మీరు నిర్మాణాత్మకంగా, ఒక ఈతగాడు. ఒక వ్యక్తి, ఒక కుక్క, మరియు ఒక బాతు అన్నీ ఈతగాళ్లు కావచ్చు.
ఈ భావనను అధికారికం చేయడానికి ABCలను ఉపయోగించవచ్చు. వారసత్వాన్ని బలవంతం చేయడానికి బదులుగా, అవసరమైన ప్రోటోకాల్ను అమలు చేస్తే ఇతర క్లాస్లను దాని వర్చువల్ సబ్క్లాస్లుగా గుర్తించే ఒక ABCని మనం నిర్వచించవచ్చు. ఇది ఒక ప్రత్యేక మ్యాజిక్ మెథడ్ ద్వారా సాధించబడుతుంది: `__subclasshook__`.
మీరు `isinstance(obj, MyABC)` లేదా `issubclass(SomeClass, MyABC)` అని పిలిచినప్పుడు, పైథాన్ మొదట స్పష్టమైన వారసత్వాన్ని తనిఖీ చేస్తుంది. అది విఫలమైతే, `MyABC`కి `__subclasshook__` మెథడ్ ఉందో లేదో తనిఖీ చేస్తుంది. ఉంటే, పైథాన్ దానిని పిలిచి, "హే, మీరు ఈ క్లాస్ను మీ సబ్క్లాస్గా పరిగణిస్తున్నారా?" అని అడుగుతుంది. ఇది ABCకి నిర్మాణం ఆధారంగా దాని సభ్యత్వ ప్రమాణాలను నిర్వచించడానికి అనుమతిస్తుంది.
ఉదాహరణ: ఒక `Serializable` ప్రోటోకాల్
ఒక డిక్షనరీకి సీరియలైజ్ చేయగల ఆబ్జెక్ట్ల కోసం ఒక ప్రోటోకాల్ను నిర్వచిద్దాం. మన సిస్టమ్లోని ప్రతి సీరియలైజబుల్ ఆబ్జెక్ట్ ఒక సాధారణ బేస్ క్లాస్ నుండి వారసత్వం పొందాలని మనం బలవంతం చేయకూడదు. అవి డేటాబేస్ మోడల్స్, డేటా ట్రాన్స్ఫర్ ఆబ్జెక్ట్స్, లేదా సాధారణ కంటైనర్లు కావచ్చు.
import abc
class Serializable(abc.ABC):
@abc.abstractmethod
def to_dict(self) -> dict:
pass
@classmethod
def __subclasshook__(cls, C):
if cls is Serializable:
# Check if 'to_dict' is in the method resolution order of C
if any("to_dict" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented
ఇప్పుడు, కొన్ని క్లాస్లను సృష్టిద్దాం. ముఖ్యంగా, వాటిలో ఏవీ `Serializable` నుండి వారసత్వం పొందవు.
class User:
def __init__(self, name: str, email: str):
self.name = name
self.email = email
def to_dict(self) -> dict:
return {"name": self.name, "email": self.email}
class Product:
def __init__(self, sku: str, price: float):
self.sku = sku
self.price = price
# ఈ క్లాస్ ప్రోటోకాల్కు అనుగుణంగా లేదు
class Configuration:
def __init__(self, setting: str):
self.setting = setting
వాటిని మన ప్రోటోకాల్తో తనిఖీ చేద్దాం:
print(f"Is User serializable? {isinstance(User('Test', 't@t.com'), Serializable)}")
print(f"Is Product serializable? {isinstance(Product('T-1000', 99.99), Serializable)}")
print(f"Is Configuration serializable? {isinstance(Configuration('ON'), Serializable)}")
# అవుట్పుట్:
# Is User serializable? True
# Is Product serializable? False <- ఆగండి, ఎందుకు? దీనిని సరిచేద్దాం.
# Is Configuration serializable? False
ఆహ్, ఒక ఆసక్తికరమైన బగ్! మన `Product` క్లాస్కు `to_dict` మెథడ్ లేదు. దానిని జోడిద్దాం.
class Product:
def __init__(self, sku: str, price: float):
self.sku = sku
self.price = price
def to_dict(self) -> dict: # మెథడ్ను జోడిస్తున్నాము
return {"sku": self.sku, "price": self.price}
print(f"Is Product now serializable? {isinstance(Product('T-1000', 99.99), Serializable)}")
# అవుట్పుట్:
# Is Product now serializable? True
`User` మరియు `Product` లకు (`object` తప్ప) ఏ సాధారణ పేరెంట్ క్లాస్ లేనప్పటికీ, మన సిస్టమ్ వాటిని రెండింటినీ `Serializable`గా పరిగణించగలదు ఎందుకంటే అవి ప్రోటోకాల్ను నెరవేరుస్తాయి. ఇది డీకప్లింగ్ కోసం చాలా శక్తివంతమైనది.
ప్రోటోకాల్ విధానం యొక్క లాభాలు మరియు నష్టాలు
లాభాలు:
- గరిష్ట సౌలభ్యం: అత్యంత వదులుగా ఉండే కప్లింగ్ను ప్రోత్సహిస్తుంది. భాగాలు కేవలం ప్రవర్తన గురించి మాత్రమే శ్రద్ధ వహిస్తాయి, ఇంప్లిమెంటేషన్ వంశం గురించి కాదు.
- అనుకూలత: ఇప్పటికే ఉన్న కోడ్ను, ముఖ్యంగా థర్డ్-పార్టీ లైబ్రరీల నుండి, అసలు కోడ్ను మార్చకుండా మీ సిస్టమ్ యొక్క ఇంటర్ఫేస్లకు సరిపోయేలా చేయడానికి ఇది ఖచ్చితంగా సరిపోతుంది.
- కంపోజిషన్ను ప్రోత్సహిస్తుంది: లోతైన, దృఢమైన వారసత్వ చెట్ల ద్వారా కాకుండా స్వతంత్ర సామర్థ్యాల నుండి వస్తువులను నిర్మించే డిజైన్ శైలిని ప్రోత్సహిస్తుంది.
నష్టాలు:
- పరోక్ష ఒప్పందం: ఒక క్లాస్ మరియు అది అమలు చేసే ప్రోటోకాల్ మధ్య సంబంధం క్లాస్ నిర్వచనం నుండి వెంటనే స్పష్టంగా కనిపించదు. ఒక `User` ఆబ్జెక్ట్ను `Serializable`గా ఎందుకు పరిగణిస్తున్నారో అర్థం చేసుకోవడానికి ఒక డెవలపర్కు కోడ్బేస్లో వెతకవలసి రావచ్చు.
- రన్టైమ్ ఓవర్హెడ్: `isinstance` చెక్ నెమ్మదిగా ఉండవచ్చు ఎందుకంటే అది `__subclasshook__`ను ప్రారంభించి, క్లాస్ యొక్క మెథడ్స్పై తనిఖీలు చేయవలసి ఉంటుంది.
- సంక్లిష్టతకు అవకాశం: ప్రోటోకాల్లో బహుళ మెథడ్స్, ఆర్గ్యుమెంట్స్, లేదా రిటర్న్ టైప్స్ ఉంటే `__subclasshook__` లోపల లాజిక్ చాలా సంక్లిష్టంగా మారవచ్చు.
ఆధునిక సంశ్లేషణ: `typing.Protocol` మరియు స్టాటిక్ ఎనాలిసిస్
పెద్ద-స్థాయి సిస్టమ్లలో పైథాన్ వాడకం పెరిగేకొద్దీ, మెరుగైన స్టాటిక్ ఎనాలిసిస్ కోసం ఆకాంక్ష కూడా పెరిగింది. `__subclasshook__` విధానం శక్తివంతమైనది కానీ ఇది పూర్తిగా రన్టైమ్ మెకానిజం. మనం కోడ్ను రన్ చేయడానికి *ముందే* స్ట్రక్చరల్ టైపింగ్ యొక్క ప్రయోజనాలను పొందగలిగితే ఎలా ఉంటుంది?
ఇది PEP 544లో `typing.Protocol` యొక్క పరిచయానికి దారితీసింది. ఇది ప్రాథమికంగా Mypy, Pyright, లేదా PyCharm యొక్క ఇన్స్పెక్టర్ వంటి స్టాటిక్ టైప్ చెక్కర్ల కోసం ఉద్దేశించిన ప్రోటోకాల్లను నిర్వచించడానికి ఒక ప్రామాణికమైన మరియు సొగసైన మార్గాన్ని అందిస్తుంది.
ఒక `Protocol` క్లాస్ మన `__subclasshook__` ఉదాహరణ మాదిరిగానే పనిచేస్తుంది కానీ బాయిలర్ప్లేట్ లేకుండా. మీరు కేవలం మెథడ్స్ మరియు వాటి సిగ్నేచర్లను నిర్వచిస్తారు. సరిపోయే మెథడ్స్ మరియు సిగ్నేచర్లు ఉన్న ఏ క్లాస్ అయినా స్టాటిక్ టైప్ చెక్కర్ ద్వారా నిర్మాణాత్మకంగా అనుకూలమైనదిగా పరిగణించబడుతుంది.
ఉదాహరణ: ఒక `Quacker` ప్రోటోకాల్
క్లాసిక్ డక్ టైపింగ్ ఉదాహరణను, కానీ ఆధునిక టూలింగ్తో మళ్లీ చూద్దాం.
from typing import Protocol
class Quacker(Protocol):
def quack(self, volume: int) -> str:
"""Produces a quacking sound."""
... # Note: The body of a protocol method is not needed
class Duck:
def quack(self, volume: int) -> str:
return f"QUACK! (at volume {volume})"
class Dog:
def bark(self, volume: int) -> str:
return f"WOOF! (at volume {volume})"
def make_sound(animal: Quacker):
print(animal.quack(10))
make_sound(Duck()) # స్టాటిక్ ఎనాలిసిస్ పాస్ అవుతుంది
make_sound(Dog()) # స్టాటిక్ ఎనాలిసిస్ ఫెయిల్ అవుతుంది!
మీరు ఈ కోడ్ను Mypy వంటి టైప్ చెక్కర్ ద్వారా రన్ చేస్తే, అది `make_sound(Dog())` లైన్ను ఒక ఎర్రర్తో ఫ్లాగ్ చేస్తుంది: `Argument 1 to "make_sound" has incompatible type "Dog"; expected "Quacker"`. `Dog` క్లాస్కు `quack` మెథడ్ లేనందున అది `Quacker` ప్రోటోకాల్ను నెరవేర్చదని టైప్ చెక్కర్ అర్థం చేసుకుంటుంది. ఇది కోడ్ ఎగ్జిక్యూట్ కాకముందే ఎర్రర్ను పట్టుకుంటుంది.
`@runtime_checkable`తో రన్టైమ్ ప్రోటోకాల్స్
డిఫాల్ట్గా, `typing.Protocol` కేవలం స్టాటిక్ ఎనాలిసిస్ కోసం మాత్రమే. మీరు దానిని రన్టైమ్ `isinstance` చెక్లో ఉపయోగించడానికి ప్రయత్నిస్తే, మీకు ఎర్రర్ వస్తుంది.
# isinstance(Duck(), Quacker) # -> TypeError: Protocol 'Quacker' cannot be instantiated
అయితే, మీరు `@runtime_checkable` డెకరేటర్తో స్టాటిక్ ఎనాలిసిస్ మరియు రన్టైమ్ ప్రవర్తన మధ్య అంతరాన్ని పూరించవచ్చు. ఇది ముఖ్యంగా పైథాన్కు మీ కోసం `__subclasshook__` లాజిక్ను ఆటోమేటిక్గా జెనరేట్ చేయమని చెబుతుంది.
from typing import Protocol, runtime_checkable
@runtime_checkable
class Quacker(Protocol):
def quack(self, volume: int) -> str: ...
class Duck:
def quack(self, volume: int) -> str: return "..."
print(f"Is Duck an instance of Quacker? {isinstance(Duck(), Quacker)}")
# అవుట్పుట్:
# Is Duck an instance of Quacker? True
ఇది మీకు రెండు ప్రపంచాలలోని ఉత్తమమైన వాటిని అందిస్తుంది: స్టాటిక్ ఎనాలిసిస్ కోసం శుభ్రమైన, డిక్లరేటివ్ ప్రోటోకాల్ నిర్వచనాలు, మరియు అవసరమైనప్పుడు రన్టైమ్ ధృవీకరణ కోసం ఎంపిక. అయితే, ప్రోటోకాల్స్పై రన్టైమ్ చెక్స్ ప్రామాణిక `isinstance` కాల్స్ కంటే నెమ్మదిగా ఉంటాయని గుర్తుంచుకోండి, కాబట్టి వాటిని వివేకంతో ఉపయోగించాలి.
ఆచరణాత్మక నిర్ణయం తీసుకోవడం: ఒక గ్లోబల్ డెవలపర్ గైడ్
కాబట్టి, మీరు ఏ విధానాన్ని ఎంచుకోవాలి? సమాధానం పూర్తిగా మీ నిర్దిష్ట వినియోగ కేస్పై ఆధారపడి ఉంటుంది. అంతర్జాతీయ సాఫ్ట్వేర్ ప్రాజెక్ట్లలో సాధారణ దృశ్యాల ఆధారంగా ఇక్కడ ఒక ఆచరణాత్మక గైడ్ ఉంది.
దృశ్యం 1: గ్లోబల్ SaaS ఉత్పత్తి కోసం ఒక ప్లగిన్ ఆర్కిటెక్చర్ను నిర్మించడం
మీరు ఒక సిస్టమ్ను డిజైన్ చేస్తున్నారు (ఉదా., ఒక ఇ-కామర్స్ ప్లాట్ఫారమ్, ఒక CMS) ఇది ప్రపంచవ్యాప్తంగా ఫస్ట్-పార్టీ మరియు థర్డ్-పార్టీ డెవలపర్లచే విస్తరించబడుతుంది. ఈ ప్లగిన్లు మీ కోర్ అప్లికేషన్తో లోతుగా ఇంటిగ్రేట్ కావాలి.
- సిఫార్సు: ఫార్మల్ ఇంటర్ఫేస్ (నామినల్ `abc.ABC`).
- కారణం: స్పష్టత, స్థిరత్వం, మరియు సుస్పష్టత చాలా ముఖ్యమైనవి. ప్లగిన్ డెవలపర్లు మీ `BasePlugin` ABC నుండి వారసత్వం పొందడం ద్వారా స్పృహతో ఎంచుకోవాల్సిన ఒక చర్చకురాని ఒప్పందం మీకు అవసరం. ఇది మీ APIని సుస్పష్టంగా చేస్తుంది. మీరు బేస్ క్లాస్లో అవసరమైన సహాయక మెథడ్స్ను (ఉదా., లాగింగ్, కాన్ఫిగరేషన్ యాక్సెస్, అంతర్జాతీయీకరణ కోసం) కూడా అందించవచ్చు, ఇది మీ డెవలపర్ ఎకోసిస్టమ్కు ఒక పెద్ద ప్రయోజనం.
దృశ్యం 2: బహుళ, సంబంధం లేని APIల నుండి ఆర్థిక డేటాను ప్రాసెస్ చేయడం
మీ ఫిన్టెక్ అప్లికేషన్కు వివిధ గ్లోబల్ పేమెంట్ గేట్వేల నుండి లావాదేవీల డేటాను వినియోగించుకోవాలి: Stripe, PayPal, Adyen, మరియు బహుశా లాటిన్ అమెరికాలో Mercado Pago వంటి ప్రాంతీయ ప్రొవైడర్. వారి SDKల ద్వారా తిరిగి వచ్చే ఆబ్జెక్ట్లు పూర్తిగా మీ నియంత్రణలో లేవు.
- సిఫార్సు: ప్రోటోకాల్ (`typing.Protocol`).
- కారణం: ఈ థర్డ్-పార్టీ SDKల సోర్స్ కోడ్ను మీ `Transaction` బేస్ క్లాస్ నుండి వారసత్వం పొందేలా మీరు మార్చలేరు. అయితే, వాటి లావాదేవీ ఆబ్జెక్ట్లలో ప్రతిదానికి `get_id()`, `get_amount()`, మరియు `get_currency()` వంటి మెథడ్స్ ఉన్నాయని మీకు తెలుసు, వాటి పేర్లు కొద్దిగా భిన్నంగా ఉన్నప్పటికీ. మీరు ఏకీకృత వీక్షణను సృష్టించడానికి `TransactionProtocol`తో పాటు అడాప్టర్ ప్యాటర్న్ను ఉపయోగించవచ్చు. ఒక ప్రోటోకాల్ మీకు అవసరమైన డేటా యొక్క *ఆకారాన్ని* నిర్వచించడానికి అనుమతిస్తుంది, ఇది ప్రోటోకాల్కు సరిపోయేలా మార్చగలిగినంత వరకు, ఏ డేటా సోర్స్తోనైనా పనిచేసే ప్రాసెసింగ్ లాజిక్ను వ్రాయడానికి మిమ్మల్ని అనుమతిస్తుంది.
దృశ్యం 3: ఒక పెద్ద, మోనోలిథిక్ లెగసీ అప్లికేషన్ను రీఫ్యాక్టర్ చేయడం
మీరు ఒక లెగసీ మోనోలిత్ను ఆధునిక మైక్రోసర్వీస్లుగా విభజించే పనిని అప్పగించారు. ఇప్పటికే ఉన్న కోడ్బేస్ డిపెండెన్సీల చిక్కుముడి, మరియు మీరు ఒకేసారి అన్నీ తిరిగి వ్రాయకుండా స్పష్టమైన సరిహద్దులను ప్రవేశపెట్టాలి.
- సిఫార్సు: ఒక మిశ్రమం, కానీ ప్రోటోకాల్స్పై ఎక్కువగా మొగ్గు చూపండి.
- కారణం: క్రమంగా రీఫ్యాక్టరింగ్ కోసం ప్రోటోకాల్స్ ఒక అసాధారణమైన సాధనం. మీరు `typing.Protocol` ఉపయోగించి కొత్త సర్వీస్ల మధ్య ఆదర్శవంతమైన ఇంటర్ఫేస్లను నిర్వచించడం ద్వారా ప్రారంభించవచ్చు. అప్పుడు, మీరు కోర్ లెగసీ కోడ్ను వెంటనే మార్చకుండానే ఈ ప్రోటోకాల్స్కు అనుగుణంగా మోనోలిత్ యొక్క భాగాల కోసం అడాప్టర్లను వ్రాయవచ్చు. ఇది భాగాలను క్రమంగా డీకపుల్ చేయడానికి మిమ్మల్ని అనుమతిస్తుంది. ఒక భాగం పూర్తిగా డీకపుల్ చేయబడి, కేవలం ప్రోటోకాల్ ద్వారా కమ్యూనికేట్ చేసిన తర్వాత, అది దాని స్వంత సర్వీస్లోకి సంగ్రహించడానికి సిద్ధంగా ఉంటుంది. కొత్త, శుభ్రమైన సర్వీస్లలో కోర్ మోడల్స్ను నిర్వచించడానికి తరువాత ఫార్మల్ ABCలను ఉపయోగించవచ్చు.
ముగింపు: మీ కోడ్లోకి అబ్స్ట్రాక్షన్ను అల్లడం
పైథాన్ యొక్క అబ్స్ట్రాక్ట్ బేస్ క్లాసెస్ భాష యొక్క ఆచరణాత్మక రూపకల్పనకు నిదర్శనం. అవి సాంప్రదాయ ఆబ్జెక్ట్-ఓరియంటెడ్ ప్రోగ్రామింగ్ యొక్క నిర్మాణాత్మక క్రమశిక్షణను మరియు డక్ టైపింగ్ యొక్క డైనమిక్ సౌలభ్యాన్ని గౌరవించే అబ్స్ట్రాక్షన్ కోసం ఒక అధునాతన టూల్కిట్ను అందిస్తాయి.
ఒక పరోక్ష ఒప్పందం నుండి ఒక అధికారిక ఒప్పందానికి ప్రయాణం ఒక పరిణితి చెందుతున్న కోడ్బేస్కు సంకేతం. ABCల యొక్క రెండు తత్వాలను అర్థం చేసుకోవడం ద్వారా, మీరు శుభ్రమైన, మరింత నిర్వహించదగిన, మరియు అత్యంత స్కేలబుల్ అప్లికేషన్లకు దారితీసే సమాచారయుక్త ఆర్కిటెక్చరల్ నిర్ణయాలు తీసుకోవచ్చు.
ముఖ్యమైన అంశాలను సంగ్రహించడానికి:
- ఫార్మల్ ఇంటర్ఫేస్ డిజైన్ (నామినల్ టైపింగ్): మీకు స్పష్టమైన, సుస్పష్టమైన, మరియు కనుగొనగలిగే ఒప్పందం అవసరమైనప్పుడు ప్రత్యక్ష వారసత్వంతో `abc.ABC`ని ఉపయోగించండి. ఇది ఫ్రేమ్వర్క్లు, ప్లగిన్ సిస్టమ్లు, మరియు మీరు క్లాస్ హైరార్కీని నియంత్రించే పరిస్థితులకు అనువైనది. ఇది డిక్లరేషన్ ద్వారా ఒక క్లాస్ ఏమిటి అనే దాని గురించి.
- ప్రోటోకాల్ ఇంప్లిమెంటేషన్ (స్ట్రక్చరల్ టైపింగ్): మీకు సౌలభ్యం, డీకప్లింగ్, మరియు ఇప్పటికే ఉన్న కోడ్ను స్వీకరించగల సామర్థ్యం అవసరమైనప్పుడు `typing.Protocol`ని ఉపయోగించండి. ఇది బాహ్య లైబ్రరీలతో పనిచేయడానికి, లెగసీ సిస్టమ్లను రీఫ్యాక్టర్ చేయడానికి, మరియు ప్రవర్తనా పాలిమార్ఫిజం కోసం డిజైన్ చేయడానికి ఖచ్చితంగా సరిపోతుంది. ఇది దాని నిర్మాణం ద్వారా ఒక క్లాస్ ఏమి చేయగలదు అనే దాని గురించి.
ఒక ఇంటర్ఫేస్ మరియు ఒక ప్రోటోకాల్ మధ్య ఎంపిక కేవలం ఒక సాంకేతిక వివరాలు కాదు; ఇది మీ సాఫ్ట్వేర్ ఎలా అభివృద్ధి చెందుతుందో ఆకృతి చేసే ఒక ప్రాథమిక డిజైన్ నిర్ణయం. రెండింటిలోనూ నైపుణ్యం సాధించడం ద్వారా, మీరు శక్తివంతమైన మరియు సమర్థవంతమైనదే కాకుండా, మార్పుల నేపథ్యంలో సొగసైన మరియు స్థితిస్థాపకమైన పైథాన్ కోడ్ను వ్రాయడానికి మిమ్మల్ని మీరు సన్నద్ధం చేసుకుంటారు.